// Swarm Model for ems tossim
// by Mina Jung @ syracuse university
// April 2010

#import "TossimModelSwarm.h"
#import <random.h>
#import <space.h>
#import <activity.h>
#import <math.h>
#import <stdio.h>

#import <stdlib.h>


@implementation TossimModelSwarm

// These methods provide access to the objects inside the ModelSwarm.

- getSensorList
{
  return moteList;
}

- (id <Grid2d>)getWorld
{
  return world;
}

- (SensorSpace *)getSensorSpace
{
  return sensorSpace;
}

- (void)_syncUpdateOrder_
{
  if (actionForEach)
    [actionForEach setDefaultOrder:
                     (id <Symbol>) (randomizeNodeUpdateOrder
                                    ? Randomized
                                    : Sequential)];
}

- (BOOL)toggleRandomizedOrder
{
  randomizeNodeUpdateOrder = !randomizeNodeUpdateOrder;
  [self _syncUpdateOrder_];
  return randomizeNodeUpdateOrder;
}

- addNewSensor: (Sensor *)mote
{
  [moteList addLast: mote];
  return self;
}


//------------------------------------------------------------------------------
//                      Create Model with default dismulation parameters
//------------------------------------------------------------------------------


+ createBegin: aZone
{
  TossimModelSwarm *obj;
  id <CustomProbeMap> probeMap;

  FILE *f;

  obj = [super createBegin: aZone];


  // tossim_conf : configuration file
  // format:
  //         number of sensor nodes
  //         maximum Battery amount
  //         world size (x & y) - environmental info
  //         number of applications



  f = fopen("./data/tossim_conf.txt", "r");
  if(f != NULL)
  {
    int nNode,xSize, ySize, nApp, nEnv, setEnv;
    double mBat;

    fscanf(f, "%d %lf %d %d %d %d %d", &nNode, &mBat, &xSize, &ySize, &nApp, &nEnv, &setEnv); 
    obj->numSensors = nNode;
    obj->maxBattery = mBat;
    obj->worldXSize = xSize;
    obj->worldYSize = ySize;
    obj->numApplications = nApp;
    obj->numEnvironmentType = nEnv;
    obj->randomEnvironmentSet = setEnv;
  }
  else 
  {
    obj->numSensors = 9;
    obj->maxBattery = 2000.0;
    obj->worldXSize = 80;
    obj->worldYSize = 80;
    obj->numApplications = 1;
    obj->numEnvironmentType = 1;
    obj->randomEnvironmentSet = 1;
  }

  

  fclose(f);


printf("\nModelSwarm -- Before probeMap creation");

  obj->randomizeNodeUpdateOrder = NO;

  // build a customized probe map. 
  // "toggleRandomizedOrder" and "addNewSensor" methods for a probe demo.

/*
  probeMap = [EmptyProbeMap createBegin: aZone];
  [probeMap setProbedClass: [self class]];
  probeMap = [probeMap createEnd];

 
  [probeMap addProbe: [probeLibrary getProbeForMessage: "toggleRandomizedOrder" inClass: [self class]]];

//  [probeMap addProbe: [probeLibrary getProbeForMessage: "addNewSensor" inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "numSensors" inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "maxBattery" inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "worldXSize" inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "worldYSize" inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "numApplications" inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "numEnvironmentType" inClass: [self class]]];

  [probeMap addProbe: [probeLibrary getProbeForVariable: "randomEnvironmentSet" inClass: [self class]]];

  [probeLibrary setProbeMap: probeMap For: [self class]];
  
*/

 probeMap =
    [CustomProbeMap create: aZone
                    forClass: [self class]
                    withIdentifiers:  "numSensors","maxBattery",
                    "worldXSize", "worldYSize",
                    "numApplications", "numEnvironmentType", "randomEnvironmentSet",
                    ":", "toggleRandomizedOrder", "addNewSensor:", NULL];

  [probeLibrary setProbeMap: probeMap For: [self class]];


printf("\nModelSwarm -- ProbeMap created");


  return obj;
}

- createEnd
{
  return [super createEnd];
}

//------------------------------------------------------------------------------
//                      Build Tossim Model Objects 
//------------------------------------------------------------------------------

- buildObjects
{


  [super buildObjects];
  
//printf("\nModelSwarm -- BuildObjects In");

  // The sensorspace represents the spatial property of mote.
  // It is initialized via various model parameters.
  // still confusing...

  sensorSpace = [SensorSpace createBegin: self];

  [sensorSpace setSizeX: worldXSize Y: worldYSize];
  
  sensorSpace = [sensorSpace createEnd];


  [sensorSpace initializeEnvironment];


  // need to set wall!!!

//printf("\nModelSwarm -- get Sensor Space by creating it");
  
  // Now set up the grid used to represent agent position


  world = [sensorSpace getSensorGrid];

//printf("\nModelSwarm -- get world (2D grid)");

  // Uniform random number generator create
  uniformInt = [UniformIntegerDist create: self setGenerator: randomGenerator];

  // Create a list to keep track of the motes in the model.

  moteList = [List create: self];

//printf("\nModelSwarm -- create List (mote)");

  // Create Sensor Nodes..
  
  [world setOverwriteWarnings: 0];
//  [[sensorSpace getSensorGrid] setOverwriteWarnings: 0];

  //++++++++++++++++++ EMS TOSSIM ++++++++++++++++++++++++++++++++++

  envF =  NULL;


  tossim = new Tossim(NULL);
//  tossim->addChannel("Building", stdout);
//  tossim->addChannel("Scheduler", stdout);

  radio = tossim->radio();

  [self setEnvironmentalFiles];
  [self setApplications];
  [self setSensorNodes];
  [self setTopology];
  [self setNoiseInterference];

  [world setOverwriteWarnings: 1];		  
//  [[sensorSpace getSensorGrid] setOverwriteWarnings: 1];		  
  
//printf("\nModelSwarm -- after TOSSIM CONFIG");


  shuffler = [ListShuffler createBegin: self];
  [shuffler setUniformRandom: uniformUnsRand];
  shuffler = [shuffler createEnd];


  return self;
}


//------------------------------------------------------------------------------
//                      Build Actions of Tossim Model Schedule 
//------------------------------------------------------------------------------

- buildActions
{
  [super buildActions];
  
  modelActions = [ActionGroup create: self];

  [modelActions createActionTo: sensorSpace message: M(updateEnvironmentValues)];

  [modelActions createActionTo: shuffler message: M(shuffleWholeList:) : moteList];

  actionForEach =
    [modelActions createActionForEach: moteList message: M(step)];

  [self _syncUpdateOrder_];


  
  modelSchedule = [Schedule create: self setRepeatInterval: 1];
  [modelSchedule at: 0 createAction: modelActions];

  return self;
}

// Now set up the model's activation. swarmContext indicates where
// we're being started in - typically, this model is run as a subswarm
// of an observer swarm.

//------------------------------------------------------------------------------
//                      Set up Tossim Model Activation 
//------------------------------------------------------------------------------
- (id <Activity>)activateIn: swarmContext
{

  [super activateIn: swarmContext];

  [modelSchedule activateIn: self];

  return [self getActivity];
}

//++++++++++++++++++ EMS TOSSIM ++++++++++++++++++++++++++++++++++
- updateTossim: (int)sec
{
  id index, mote;
  double value;
  double max;
  double total = 0;
  int num;

  int idx=0, i, aid;

  currentTime = sec;


  // Update Environmental information
  [self updateEnvironment: sec];

  while(tossim->runNextEventSec(sec));

  index = [moteList begin: [self getZone]];

  while( (mote = [index next]) )
  {
    SensorState mote_state;
    idx = [mote getSensorId];
    aid = [mote getAppId];
    max = [mote getMaxBattery];

    mote_state = [mote getSensorState];

    if( mote_state == Die) // already die.. no more processing
      continue;
    else if(mote_state == NotActive && tossim->getNode(idx)->isOn())
    {
      [mote setSensorState: Active]; // the mote is turned on!! alleh!
    }

    total = 0.0;
    
    value = tossim->getBatterySec(idx, sec);
    [mote setAmtBattery: value];

    if(value < 0.03*max)  // when battery is less than 1%, turn the mote off
    {
      tossim->getNode(idx)->turnOff();
      [mote setSensorState: Die]; // die..
    }

    for( i = 0; i < 3; i++ )
    {
      value = tossim->getCurrentSec(idx, i);
      [mote setTotCurrentType: i To: value];
      total += value;
    }

    [mote setTotalCurrent: total];

    num = tossim->getSentMsg(idx);
    [mote setSentMsgs : num];

    num = tossim->getRecvMsg(idx);
    [mote setRecvMsgs : num];
  }

  [index drop];

  return self;
}

- updateEnvironment: (int)sec
{
  id index, mote;

  int idx=0, aid;

  int t, n, i;
  int a, x, y, val;


  // use random number generator to set environment values
  if(randomEnvironmentSet)
  {
      int numAbnormal;
      int ablist[16] = {0};


      // normal environment values
      for(y = 0; y < worldYSize; y++)
        for(x = 0; x < worldXSize; x++)
        {
          //val = [self getRandomIntMin: min Max: max];

          val = 0;
          [sensorSpace updateEnvironment: val atX: x Y: y A: t];
        }

      // abnormal evironment values

      numAbnormal = [self getRandomIntMin: 3 Max: 10];

      for(i = 0; i < numAbnormal; )
      {
        n = [self getRandomIntMin: 1 Max: 15];

        if(ablist[n] == 0){
          ablist[n] = 1;
          i++;
        }
      }




      index = [moteList begin: [self getZone]];

      while( (mote = [index next]) )
      {
        SensorState mote_state;
        idx = [mote getSensorId];
        aid = [mote getAppId];

        if(ablist[idx])
        {
          int xpos, ypos, radius;
          int xstart, xend, ystart, yend;

          xpos   = [mote getX];
          ypos   = [mote getY];

          radius = [self getRandomIntMin: 3 Max: 9];


          xstart = xpos - radius;
          if(xstart < 0)
            xstart = 0;
          xend = xpos + radius;
          if(xend >= worldXSize)
            xend = worldXSize - 1;

          ystart = ypos - radius;
          if(ystart < 0)
            ystart = 0;
          yend = ypos + radius;
          if(yend >= worldYSize)
            yend = worldYSize - 1;


          switch(aid)
          {
          case 1:
                 t = 1;
                 break;
          case 2:
                 t = 2;
                 break;
          case 3:
                 t = [self getRandomIntMin:1 Max:3];
                 break;
          }

       	  val = 1;
          for(y = ystart; y <= yend; y++)
            for(x = xstart; x <= xend; x++)
              [sensorSpace updateEnvironment: val atX: x Y: y A: t];
        }
      }

      [index drop];

  }
  // read environmental data
  else if(envF) 
  {
    if(fscanf(envF, "%d %d", &t, &n) == EOF)
    {
      envF = NULL;
    }
    else if(t == sec)
    {
      for(i = 0; i < n; i++) 
      {
        fscanf(envF, "%d %d %d %d", &a, &x, &y, &val);

        if(x == -1 && y == -1)
        {
          for(y = 0; y < worldYSize; y++)
            for(x = 0; x < worldXSize; x++)
              [sensorSpace updateEnvironment: val atX: x Y: y A: a];
        }
        else
        {
          [sensorSpace updateEnvironment: val atX: x Y: y A: a];
        }
      }
    }
  }

  index = [moteList begin: [self getZone]];

  while( (mote = [index next]) )
  {
    SensorState mote_state;
    idx = [mote getSensorId];
    aid = [mote getAppId];

    mote_state = [mote getSensorState];

    if( mote_state == Die) // already die.. no more processing
      continue;
    else if(mote_state == NotActive && tossim->getNode(idx)->isOn())
    {
printf("\nUpdate Env : Activate Sensor");
      [mote setSensorState: Active]; // the mote is turned on!! alleh!
    }


    for(t = 1; t <= numEnvironmentType; t++)  
    {
      [mote updateSensedValue: t];

      val = [mote getSensedValue: t];
    
      tossim->getNode(idx)->setSwarmEnvValue(t, val);

      switch(aid)
      {
        case 1: // Motion Detector

               if( val > 0 )
                 tossim->getNode(idx)->setSwarmEnvInterrupt(t, currentTime);
               break;

        case 2: // Camera Sensor

               if( val > 0 )
                 tossim->getNode(idx)->setSwarmEnvInterrupt(t, currentTime);
               break;

        case 3: // General Sensor

               break;
      }
    }

  }

  [index drop];

    
  return self;
}

- setTopology
{
  int src, dst;
  double gain;
  char temp[10];


  FILE *f = fopen("./data/topo.txt", "r");
  if(f == NULL)
    printf("There is no topo.txt");

  while(fscanf(f, "%s %d %d %lf", temp, &src, &dst, &gain) != EOF)
  {
    if( (0 <= src && src < numSensors) && (0 <= dst && dst < numSensors) )
      radio->add(src, dst, gain);
  }

  fclose(f);

  return self;
}

- setApplications
{
  int i;

  FILE *f = fopen("./data/applications.txt","r");

  if(f){
    for(i = 0; i < numApplications; i++)
    {
      char * apps = (char *) calloc ( 20, sizeof(char) );
      fscanf(f, "%s", apps);
      applications.push_back(apps);
    }
  }
  else
  {
    char * apps;

    apps = strdup("default");
    applications.push_back(apps);
  }
  return self;
}


- setNoiseInterference
{
  int noise, i;
  Mote *node = NULL;


  FILE *f = fopen("./data/meyer-heavy.txt", "r");
  
printf("\n Set Noise Interference");
  while(fscanf(f, "%d", &noise) != EOF)
  {
    for(i = 0; i < numSensors; i++){
      node = tossim->getNode(i);
      if(!node ){
		printf("id %d noise %d --> somthing wrong!!", i, noise);
		exit(0);
	}

      tossim->getNode(i)->addNoiseTraceReading(noise);
    }
  }


  fclose(f);

  for(i = 0; i < numSensors; i++)
    tossim->getNode(i)->createNoiseModel();


  return self;
}

- setSensorNodes
{
  int idx;
  int i;
  int xloc, yloc;
  int aid;
  float initB, maxB;
  int data;
  int vision;
  long boot;
  int freq;

  // sensor node configuration
  //       node id
  //       (x, y) location
  //       application id
  //       initial capacity of Battery
  //       max capacity of Battery
  //       sensing data type -1 : no sensing app
  //       vision length
  //       boot time

  FILE *f = fopen("./data/node_conf.txt", "r");

//  tossim->addChannel("SimMoteP", stdout);

  for(i = 0; i < numSensors; i++)
  {
  
    Sensor * mote;
    Mote * node = NULL;

    fscanf(f, "%d %d %d %d %f %f %d %d %ld %d", &idx, &xloc, &yloc, &aid, &initB, &maxB, &data, &vision, &boot, &freq);

    printf("\nXsize %d YSize %d Sensor ID : %d\t%d %d %d %d %.2f %.2f", worldXSize, worldYSize, idx, xloc, yloc, aid, vision, initB, maxB);

    if(idx >= numSensors || idx < 0)
      continue;

    if(xloc < 0 || xloc >= worldXSize)
      continue;

    if(yloc < 0 || yloc >= worldYSize)
      continue;
      
    mote = [Sensor createBegin: self];
    [mote setModelSwarm: self];
    mote = [mote createEnd];
      
    [moteList addLast: mote];
      
    // Now initialize the rest of the mote's state.
    [mote setSensorId: idx];
    [mote setAppId: aid];
    [mote setDataType: data];

    [mote setInitBattery: initB];
    [mote setMaxBattery: maxB];


    [mote setVision: vision];

    [mote setTimerFreq: freq];

    [mote setApplication: applications[aid-1]];

    [mote setSensorState: NotActive]; // not active yet, before booting


    [sensorSpace addSensor: mote atX: xloc Y: yloc];

    tossim->setNode(idx, aid, freq);

    tossim->setBatInfo(idx, initB, maxB);

    tossim->getNode(idx)->bootAtTime(boot);
  }

  fclose(f);



  return self;
}

- setEnvironmentalFiles
{
  int num, i, j, k;
  int type, bot, top;
  int x, y, l, w;
  char dir;

  envF = fopen("./data/env_file.txt","r");

  if(envF)
  {
    fscanf(envF, "%d", &num);

    // Data TYPE

    for( i = 0; i < num; i++ )
    {
      fscanf(envF, "%d %d %d", &type, &bot, &top);
      [sensorSpace setEnvironmentRange: type Min: bot Max: top];
    }


    // Wall

    fscanf(envF, "%d", &num);

    for( i = 0; i < num; i++ )
    {
      fscanf(envF, " %c %d %d %d %d", &dir, &w, &x, &y, &l);

      if(dir == 'v')
      { 
        for(k = 0; k < w; k++)
          for(j = 0; j < l; j++)
            [sensorSpace setWallAtX: (x+k) Y: (y+j)];
      }
      else if(dir == 'h') 
      { 
        for(k = 0; k < w; k++)
          for(j = 0; j < l; j++)
            [sensorSpace setWallAtX: (x+j) Y: (y+k)];
      }
    }

  }

//  fclose(envF);
  
  return self;
}

- (int)getRandomIntMin: (int)min Max: (int)max
{

  int num;

  if(min == max)
    num = min;

  else
    num = [uniformInt getIntegerWithMin: min withMax: max];

  return num;
}


- changeMoteBatteryCapID: (int)idx Value: (double)bat
{

  tossim->changeBatCap(idx, (float)bat);

  return self;
}
@end
